home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / readers / nn-tk.001 / nn-tk~ / nn / nntp.c < prev    next >
C/C++ Source or Header  |  1996-03-11  |  29KB  |  1,224 lines

  1. /*
  2.  * nntp module for nn.
  3.  *
  4.  * The original taken from the nntp 1.5 clientlib.c
  5.  * Modified heavily for nn.
  6.  *
  7.  * Rene' Seindal (seindal@diku.dk) Thu Dec  1 18:41:23 1988
  8.  *
  9.  * I have modified Rene's code quite a lot for 6.4 -- I hope he
  10.  * can still recognize a bit here and a byte there; in any case,
  11.  * any mistakes are mine :-)  ++Kim
  12.  */
  13.  
  14.  
  15. #include "config.h"
  16.  
  17. /*
  18.  *     nn maintains a cache of recently used articles to improve efficiency.
  19.  *     To change the size of the cache, define NNTPCACHE in config.h to be
  20.  *    the new size of this cache.
  21.  */
  22.  
  23. #ifndef NNTPCACHE
  24. #define NNTPCACHE    10
  25. #endif
  26.  
  27. #ifdef NNTP
  28. #include <stdio.h>
  29. #include "nntp.h"
  30. #include <sys/socket.h>
  31. #include <netdb.h>
  32. #include <errno.h>
  33.  
  34. #include "hash.h"
  35. #include "hdbm.h"
  36. #include "newsoverview.h"
  37.  
  38. /* This is necessary due to the definitions in m-XXX.h */
  39. #include <netinet/in.h>
  40.  
  41.  
  42.  
  43. /* nntp.c */
  44.  
  45. static void debug_msg __APROTO((char *prefix, char *str));
  46. static void io_error __APROTO((void));
  47. static int find_server __APROTO((void));
  48. static get_server_line __APROTO((char *string, int size));
  49. static get_server __APROTO((char *string, int size));
  50. static get_socket __APROTO((void));
  51. static connect_server __APROTO((void));
  52. static put_server __APROTO((char *string));
  53. static ask_server __APROTO(());
  54. static copy_text __APROTO((register FILE *fp, char *name));
  55. static do_set_group __APROTO((int fst));
  56. static struct cache *search_cache __APROTO((article_number art, group_header *gh));
  57. static struct cache *new_cache_slot __APROTO((void));
  58. static void clean_cache __APROTO((void));
  59. static sort_art_list __APROTO((register article_number *f1, register article_number *f2));
  60.  
  61. import char *db_directory, *tmp_directory, *news_active;
  62. import char *server_nntp;
  63.  
  64. export char nntp_server[256] = "";    /* name of nntp server */
  65. export int nntp_failed = 0;    /* bool: t iff connection is broken in
  66.                    nntp_get_article() or nntp_get_active() */
  67.  
  68. export int nntp_cache_size = NNTPCACHE;
  69. export char *nntp_cache_dir = NULL;
  70.  
  71. export int nntp_local_server = 0;
  72. export int nntp_debug = 0;
  73. export int nntp_readahead = 40;
  74.  
  75. import int silent, no_update;
  76. import int nntp_progress_a;
  77. import int nntp_abt;
  78. import int newsrc_only;
  79. import int group_list_unsub;
  80.  
  81. #if !defined(__FreeBSD__) && !defined(__NetBSD__)
  82. import int sys_nerr;
  83. import char *sys_errlist[];
  84. #endif
  85. extern void nn_exitmsg();
  86. extern void sys_error();
  87. extern int sys_warning();
  88.  
  89. #if defined(__FreeBSD__) || defined(__NetBSD__)
  90. #define syserr() strerror(errno)
  91. #else
  92. #define syserr() (errno >= 0 && errno < sys_nerr ? \
  93.           sys_errlist[errno] : "Unknown error.")
  94. #endif
  95.  
  96. import char *mktemp();
  97.  
  98. static FILE *nntp_in = NULL;        /* fp for reading from server */
  99. static FILE *nntp_out = NULL;        /* fp for writing to server */
  100. static int is_connected = 0;        /* bool: t iff we are connected */
  101. static group_header *group_hd;        /* ptr to servers current group */
  102. static int group_is_set = 0;        /* bool: t iff group_hd is set */
  103. static int try_again = 0;        /* bool: t if timeout forces retry */
  104. static int can_post = 0;        /* bool: t iff NNTP server accepts postings */
  105.  
  106. #define ERR_TIMEOUT    503        /* Response code for timeout */
  107.                     /* Same value as ERR_FAULT */
  108.  
  109. #ifdef NO_RENAME
  110. static rename(old, new)
  111. char *old, *new;
  112. {
  113.     if (unlink(new) < 0 && errno != ENOENT) return -1;
  114.     if (link(old, new) < 0) return -1;
  115.     return unlink(old);
  116. }
  117. #endif /* NO_RENAME */
  118.  
  119. /*
  120.  * debug_msg: print a debug message.
  121.  *
  122.  *    The master appends prefix and str to a log file, and clients
  123.  *    prints it as a message.
  124.  *
  125.  *    This is controlled via the nntp-debug variable in nn, and
  126.  *    the option -D2 (or -D3 if the normal -D option should also
  127.  *    be turned on).  Debug output from the master is written in
  128.  *    $TMP/nnmaster.log.
  129.  */
  130.  
  131. static void
  132. debug_msg(prefix, str)
  133. char *prefix, *str;
  134. {
  135. #ifdef TK
  136.     printf("NNTP%s %s\n", prefix, str);
  137. #else /*TK*/
  138.     msg("NNTP%s %s", prefix, str);
  139.     user_delay(1);
  140. #endif /*TK*/
  141. }
  142.  
  143. /*
  144.  * io_error: signal an I/O error in talking to the server.
  145.  *
  146.  *     An nn client terminates a session with the user.  The master
  147.  *    simply closes the connection.  The flag nntp_failed is set, for
  148.  *    use by the master to terminate collection.
  149.  *
  150.  *    BUG: if the nntp server is forcibly killed, errno can contain a
  151.  *    bogus value, resulting in strange error messages.  It is
  152.  *    probably better just to write out the numerical value of errno.
  153.  */
  154.  
  155. static void
  156. io_error()
  157. {
  158.     nn_exitmsg(1, "Lost connection to NNTP server %s: %s", nntp_server, syserr());
  159.         /* NOTREACHED */
  160. }
  161.  
  162. /*
  163.  * find_server: Find out which host to use as NNTP server.
  164.  *
  165.  *     This is done by consulting the file NNTP_SERVER (defined in
  166.  *     config.h).  Set nntp_server[] to the host's name.
  167.  */
  168.  
  169. static int
  170. find_server()
  171. {
  172.     char *cp, *name;
  173.     char buf[BUFSIZ];
  174.     FILE *fp;
  175.  
  176.     /*
  177.      * This feature cannot normally be enabled, because the database and
  178.      * the users rc file contains references to articles by number, and
  179.      * these numbers are not unique across NNTP servers.
  180.      */
  181.     /*
  182.      * But, since we no longer have a database to worry about, give the user
  183.      * the rope. If he wants to hang himself, then let him! :-)
  184.      * Let the user worry about keeping his .newsrc straight.
  185.      */
  186.     if ((cp = getenv("NNTPSERVER")) != NULL) {
  187.     strncpy(nntp_server, cp, sizeof nntp_server);
  188.     return 1;
  189.     }
  190.  
  191.     name = NNTP_SERVER;
  192.     if (*name != '/')
  193.     name = relative(lib_directory, name);
  194.  
  195.     if ((fp = open_file(name, OPEN_READ)) != NULL) {
  196.     while (fgets(buf, sizeof buf, fp) != 0) {
  197.         if (*buf == '#' || *buf == '\n')
  198.         continue;
  199.         if ((cp = strchr(buf, '\n')) != 0)
  200.         *cp = '\0';
  201.         strncpy(nntp_server, buf, sizeof nntp_server);
  202.         fclose(fp);
  203.         return 1;
  204.     }
  205.     fclose(fp);
  206.     }
  207.     return 0;
  208. }
  209.  
  210. /*
  211.  * get_server_line: get a line from the server.
  212.  *
  213.  *     Expects to be connected to the server.
  214.  *     The line can be any kind of line, i.e., either response or text.
  215.  *    Returns length of line if no error.
  216.  *    If error and master, then return -1, else terminate.
  217.  */
  218.  
  219. static int
  220. get_server_line(string, size)
  221.     register char *string;
  222.     register int size;
  223. {
  224.     if (fgets(string, size, nntp_in) == NULL) {
  225.     io_error();
  226.     return -1;
  227.     }
  228.  
  229.     size = strlen(string);
  230.     if (size < 2 || !(string[size-2] == '\r' && string[size-1] == '\n'))
  231.     return size;            /* XXX */
  232.  
  233.     string[size-2] = '\0';        /* nuke CRLF */
  234.     return size-2;
  235. }
  236.  
  237. /*
  238.  * get_server: get a response line from the server.
  239.  *
  240.  *     Expects to be connected to the server.
  241.  *     Returns the numerical value of the reponse, or -1 in case of errors.
  242.  */
  243.  
  244. static int
  245. get_server(string, size)
  246.     char *string;
  247.     int size;
  248. {
  249.     if (get_server_line(string, size) < 0)
  250.     return -1;
  251.  
  252.     if (nntp_debug) debug_msg("<<<", string);
  253.  
  254.     return isdigit(*string) ? atoi(string) : 0;
  255. }
  256.  
  257. /*
  258.  * get_socket:  get a connection to the nntp server.
  259.  *
  260.  * Errors can happen when YP services or DNS are temporarily down or
  261.  * hung, so we log errors and return failure rather than exitting if we
  262.  * are the master.  The effects of retrying every 15 minutes (or whatever
  263.  * the -r interval is) are not that bad.  Dave Olson, SGI
  264.  */
  265.  
  266. static int
  267. get_socket()
  268. {
  269.     int s;
  270.     struct sockaddr_in sin;
  271.     struct servent *getservbyname(), *sp;
  272.     struct hostent *gethostbyname(), *hp;
  273.  
  274. #ifdef h_addr
  275.     int     x = 0;
  276.     register char **cp;
  277. #endif /* h_addr */
  278.  
  279.     if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  280.     return sys_warning("nntp/tcp: Unknown service.\n");
  281.  
  282.     s = 2;
  283.     while ((hp = gethostbyname(nntp_server)) == NULL) {
  284.     if (--s < 0) goto host_err;
  285.     sleep(10);
  286.     }
  287.  
  288.     clearobj(&sin, sin, 1);
  289.     sin.sin_family = hp->h_addrtype;
  290.     sin.sin_port = sp->s_port;
  291.  
  292.  
  293. #ifdef h_addr
  294.     /* get a socket and initiate connection -- use multiple addresses */
  295.  
  296.     s = x = -1;
  297.     for (cp = hp->h_addr_list; cp && *cp; cp++) {
  298.     s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  299.     if (s < 0) goto sock_err;
  300. #ifdef NO_MEMCPY
  301.     bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  302. #else /* NO_MEMCPY */
  303.     memcpy((char *)&sin.sin_addr, *cp, hp->h_length);
  304. #endif /* NO_MEMCPY */
  305.  
  306.     /* Quick hack to work around interrupting system calls.. */
  307.     while((x = connect(s, (struct sockaddr *)&sin, sizeof (sin))) < 0 &&
  308.         errno == EINTR) sleep(1);
  309.     if (x == 0)
  310.         break;
  311.     msg("Connection to %s failed: %s", nntp_server, syserr());
  312.     (void) close(s);
  313.     s = -1;
  314.     }
  315.     if (x < 0)
  316.     sys_warning("Giving up on NNTP server %s!", nntp_server);
  317. #else /* h_addr */                /* no name server */
  318.     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  319.     goto sock_err;
  320.     
  321.     /* And then connect */
  322. #ifdef NO_MEMCPY
  323.     bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  324. #else /* NO_MEMCPY */
  325.     memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
  326. #endif /* NO_MEMCPY */
  327.     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0)
  328.     goto conn_err;
  329. #endif /* h_addr */
  330.     return s;
  331.  
  332.  host_err:
  333.     sys_warning("NNTP server %s unknown.\n", nntp_server);
  334.     return -1;
  335.  
  336.  sock_err:
  337.     sys_warning("Can't get NNTP socket: %s", syserr());
  338.     return -1;
  339.  
  340.  conn_err:
  341.     (void) close(s);
  342.     sys_warning("Connection to %s failed: %s", nntp_server, syserr());
  343.     return -1;
  344. }
  345.  
  346. /*
  347.  * connect_server: initialise a connection to the nntp server.
  348.  *
  349.  *     It expects nntp_server[] to be set previously, by a call to
  350.  *     nntp_check.  It is called from nntp_get_article() and
  351.  *    nntp_get_active() if there is no established connection.
  352.  */
  353.  
  354. static int
  355. connect_server()
  356. {
  357.     int sockt_rd, sockt_wr;
  358.     int response;
  359.     char line[NNTP_STRLEN];
  360.     
  361.     if (!silent)
  362.     msg("Connecting to NNTP server %s ...", nntp_server);
  363.  
  364.     nntp_failed = 1;
  365.     is_connected = 0;
  366.  
  367.     sockt_rd = get_socket();
  368.     if (sockt_rd < 0)
  369.     return -1;
  370.  
  371.     if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
  372.     close(sockt_rd);
  373.         return -1;
  374.     }
  375.     sockt_wr = dup(sockt_rd);
  376.     if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
  377.     close(sockt_wr);
  378.     fclose(nntp_in);
  379.         nntp_in = NULL;               /* from above */
  380.         return -1;
  381.     }
  382.  
  383.     /* Now get the server's signon message */
  384.     response = get_server(line, sizeof(line));
  385.  
  386.     switch (response) {
  387.     case OK_CANPOST:
  388.     can_post = 1;
  389.     break;
  390.     case OK_NOPOST:
  391.     can_post = 0;
  392.     break;
  393.     default:
  394.     nn_exitmsg(1, line);
  395.     /* NOTREACHED */
  396.     }
  397.  
  398.     /* Try and speak NNRP style reader protocol */
  399.     sprintf(line, "MODE READER");
  400.     if (put_server(line) >= 0) {
  401.         /* See what we got if from a NNRP compliant server like INN's nnrpd */
  402.         response = get_server(line, sizeof(line));
  403.  
  404.     switch (response) {
  405.     case OK_CANPOST:
  406.         can_post = 1;
  407.         break;
  408.     case OK_NOPOST:
  409.         can_post = 0;
  410.         break;
  411.     case ERR_COMMAND:
  412.     default:
  413.         /* if it doesn't understand MODE READER, we don't care.. :-) */
  414.         break;
  415.     }
  416.     }
  417.  
  418.     if (!silent)
  419.     msg("Connection to NNTP server %s ... ok (%s)",
  420.         nntp_server, can_post ? "posting is allowed" : "no posting");
  421.  
  422.  
  423.     is_connected = 1;
  424.     group_is_set = 0;
  425.     nntp_failed = 0;
  426.     try_again = 0;
  427. #ifdef NNTP
  428.     return send_user_info();    
  429. #else
  430.     return 0;
  431. #endif
  432. }
  433.  
  434.  
  435. /*
  436.  * put_server:  send a line to the nntp server.
  437.  *
  438.  *     Expects to be connected to the server.
  439.  */
  440.  
  441. static int
  442. put_server(string)
  443.     char *string;
  444. {
  445.     if (nntp_debug) debug_msg(">>>", string);
  446.  
  447.     fprintf(nntp_out, "%s\r\n", string);
  448.     if (fflush(nntp_out) == EOF) {
  449.     io_error();
  450.     return -1;
  451.     }
  452.     return 0;
  453. }
  454.  
  455. /* kill transfer by disconnecting and reconnecting to the server */
  456. void
  457. nntp_restart_server()
  458. {
  459.   (void) fclose(nntp_out);
  460.   (void) fclose(nntp_in);
  461.  
  462.   is_connected = 0;
  463.   connect_server();
  464. }
  465.  
  466. /*
  467.  * ask_server:  ask the server a question and return the answer.
  468.  *
  469.  *    Expects to be connected to the server.
  470.  *    Returns the numerical value of the reponse, or -1 in case of
  471.  *    errors.
  472.  *     If the first paramter is non-null returns the response in
  473.  *      it (has to point at a big enough buffer).
  474.  *    Contains some code to handle server timeouts intelligently.
  475.  */
  476.  
  477. /*
  478.  * LIST XXX returns fatal ERR_FAULT code if requested list does not exist
  479.  * This is only fatal for LIST ACTIVE -- else change to ERR_NOGROUPS
  480.  * (Note that some nntp servers spew a syntax error on LIST ACTIVE.TIMES
  481.  *  we change it to ERR_NOGROUPS also)
  482.  */
  483. static int fix_list_response = 0;
  484.  
  485. /*VARARGS*/
  486. static int
  487. ask_server(va_alist)
  488. va_dcl
  489. {
  490.     char buf[NNTP_STRLEN];
  491.     char *fmt, *obuf;
  492.     int response;
  493.     int fix_err;
  494.     use_vararg;
  495.  
  496.     fix_err = fix_list_response;
  497.     fix_list_response = 0;
  498.  
  499.     start_vararg;
  500.     obuf = va_arg1(char *);
  501.     fmt = va_arg2(char *);
  502.     vsprintf(buf, fmt, va_args3toN);
  503.     end_vararg;
  504.  
  505.     if (put_server(buf) < 0)
  506.     return -1;
  507.     response = get_server(buf, sizeof(buf));
  508.     if (obuf) {
  509.       strcpy(obuf,buf);
  510.     }
  511.     /*
  512.      * Handle the response from the server.  Responses are handled as
  513.      * followes:
  514.      *
  515.      * 100-199    Informational.  Passed back. (should they be ignored?).
  516.      * 200-299    Ok messages.  Passed back.
  517.      * 300-399    Ok and proceed.  Can not happen in nn.
  518.      * 400-499    Errors (no article, etc).  Passed up and handled there.
  519.      * 500-599    Fatal NNTP errors.  Handled below.
  520.      */
  521.     if (response == ERR_GOODBYE || response > ERR_COMMAND) {
  522.     if (fix_err && (response == ERR_FAULT || response == ERR_CMDSYN))
  523.         return ERR_NOGROUP;
  524.  
  525.     nntp_failed = 1;
  526.     nntp_close_server();
  527.  
  528.     if (response != ERR_TIMEOUT && response != ERR_GOODBYE) {
  529.             /* if not timeout and not goodbye, complain */
  530.         sys_error("NNTP %s response: %d", buf, response);
  531.         /* NOTREACHED */
  532.     }
  533.     if (response == ERR_GOODBYE) {
  534.         sys_warning("NNTP %s response: %d", buf, response);
  535.     }
  536.  
  537.     try_again = 1;
  538.     group_is_set = 0;
  539.     }
  540.     return response;
  541. }
  542.  
  543. /*
  544.  * copy_text: copy text response into file.
  545.  *
  546.  *     Copies a text response into an open file.
  547.  *    Return -1 on error, 0 otherwise.  It is treated as an error, if
  548.  *    the returned response it not what was expected.
  549.  */
  550.  
  551. static int last_copy_blank;
  552.  
  553. static int
  554. copy_text(fp, name)
  555. register FILE *fp;
  556. char *name;
  557. {
  558.     char buf[NNTP_STRLEN];
  559.     register char *cp;
  560.     register int nlines;
  561.  
  562.     nlines = 0;
  563.     last_copy_blank = 0;
  564.     nntp_abt = 0;
  565.     while (get_server_line(buf, sizeof buf) >= 0) {
  566.     cp = buf;
  567.     if (*cp == '.')
  568.         if (*++cp == '\0') {
  569.         if (nlines <= 0) break;
  570.         if (nntp_debug) {
  571.             sprintf(buf, "%d lines", nlines);
  572.             debug_msg("COPY", buf);
  573.         }
  574.         tk("nntp_kmsg");
  575.         return 0;
  576.         }
  577.     last_copy_blank = (*cp == NUL);
  578.     fputs(cp, fp);
  579.     putc('\n', fp);
  580.     nlines++;
  581.     if (nntp_progress_a && nlines > nntp_progress_a &&
  582.         nlines % nntp_progress_a == 0) {
  583.       tk("nntp_lmsg {Reading %s:%5d} 1",name, nlines);
  584.     }
  585.     if (nntp_abt) {
  586.       nntp_restart_server();
  587.       if (nntp_debug) {
  588.         sprintf(buf, "%d lines", nlines);
  589.         debug_msg("ABORT", buf);
  590.       }
  591.       tk("nntp_kmsg");
  592.       return 0;
  593.     }
  594.     }
  595.     fclose(fp);
  596.    tk("nntp_kmsg");
  597.     if (nntp_debug) debug_msg("COPY", "EMPTY");
  598.     return -1;
  599. }
  600.  
  601. static void
  602. info_group(gh,first,last)
  603. group_header *gh;
  604. int first, last;
  605. {
  606.   gh->group_flag |= G_RDAHEAD;
  607.   if (last > 0) {
  608.     gh->first_a_article = first;
  609.     gh->last_a_article = last;
  610.     gh->first_db_article = first;
  611.     gh->last_db_article = last;
  612.     if (gh->last_article  < first) {
  613.       gh->last_article = first - 1;
  614.     }
  615.     gh->group_flag &= ~G_UNKNOWN;
  616.     add_unread(gh, 1);
  617.   }
  618.   if (!(gh->group_flag & G_UNSUBSCRIBED) || group_list_unsub)
  619.     tk_group_ent(gh,"list_update");    
  620. }
  621.  
  622. static int
  623. do_set_group(fst)
  624. int fst;
  625. {
  626.     int n, i;
  627.     int r, l, first, last;
  628.     int response;
  629.     char buf[NNTP_STRLEN],gn[NNTP_STRLEN];
  630.     group_header *gh;
  631.  
  632.     n = 0;
  633.     if (newsrc_only &&
  634.     (!(group_hd->group_flag & G_RDAHEAD)
  635.      || (group_hd->next_group
  636.          && !(group_hd->next_group->group_flag & G_RDAHEAD)))) {
  637.       for (gh = group_hd; gh; gh = gh->next_group) {
  638.     sprintf(buf,"GROUP %s",gh->group_name);
  639.     if (put_server(buf) < 0)
  640.       printf("ERR GROUP %s\n",gh->group_name);
  641.     n++;
  642.     if (n > nntp_readahead)
  643.       break;
  644.       }
  645.       for (i = 0; i <= n - 1; i++) {
  646.     response = get_server(buf, sizeof(buf));
  647.     sscanf(buf, "%d %d %d %d %s", &r, &l, &first, &last, gn);
  648.     if (buf[0] == '2') {
  649.       gh = lookup(gn);
  650.       info_group(gh,first,last);
  651.     }
  652.       }
  653.     }
  654.  
  655.     /* don't get group info when empty group for -A mode */
  656.     if (newsrc_only && (group_hd->group_flag & G_RDAHEAD)) {
  657.       if ((group_hd->unread_count == 0 && fst > group_hd->last_a_article)
  658.        || group_hd->last_a_article == ART_MAX_DUMMY) {
  659.     return -1;
  660.       }
  661.     }
  662.  
  663.     switch (n = ask_server(buf, "GROUP %s", group_hd->group_name)) {
  664.      case OK_GROUP:
  665.     group_is_set = 1;
  666.     sscanf(buf, "%d %d %d %d %s", &r, &l, &first, &last, gn);
  667.     info_group(group_hd,first,last);
  668.     return 1;
  669.  
  670.      case ERR_NOGROUP:
  671.     log_entry('N', "NNTP: group %s not found", group_hd->group_name);
  672.     return -1;
  673.  
  674.      default:
  675.     if (try_again) return 0;    /* Handle nntp server timeouts */
  676.     break;
  677.     }
  678.     if (!nntp_failed) {
  679.     log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
  680.     nntp_failed = 1;
  681.     }
  682.     return -1;
  683. }
  684.  
  685. /*
  686.  * The following functions implements a simple lru cache of recently
  687.  * accessed articles.  It is a simple way to improve effeciency.  Files
  688.  * must be kept by name, because the rest of the code expects to be able
  689.  * to open an article multiple times, and get separate file pointers.
  690.  */
  691.  
  692. struct cache {
  693.     char        *file_name;    /* file name */
  694.     article_number    art;        /* article stored in file */
  695.     group_header    *grp;        /* from this group */
  696.     unsigned        time;        /* time last accessed */
  697. } cache[NNTPCACHE];
  698.  
  699. static unsigned time_counter = 1;        /* virtual time */
  700.  
  701. /*
  702.  * search_cache: search the cache for an (article, group) pair.
  703.  *
  704.  *     Returns a pointer to the slot where it is, null otherwise
  705.  */
  706.  
  707. static struct cache *search_cache(art, gh)
  708.     article_number art;
  709.     group_header *gh;
  710. {
  711.     struct cache *cptr = cache;
  712.     int i;
  713.  
  714.     if (nntp_cache_size > NNTPCACHE)
  715.     nntp_cache_size = NNTPCACHE;
  716.  
  717.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  718.     if (cptr->art == art && cptr->grp == gh) {
  719.         cptr->time = time_counter++;
  720.         return cptr;
  721.     }
  722.     return NULL;
  723. }
  724.  
  725. /*
  726.  * new_cache_slot: get a free cache slot.
  727.  *
  728.  *     Returns a pointer to the allocated slot.
  729.  *     Frees the old filename, and allocates a new, unused filename.
  730.  *    Cache files can also stored in a common directory defined in
  731.  *    ~/.nn or CACHE_DIRECTORY if defined in config.h.
  732.  */
  733.  
  734. static struct cache *new_cache_slot()
  735. {
  736.     register struct cache *cptr = cache;
  737.     int i, lru = 0;
  738.     unsigned min_time = time_counter;
  739.     char name[FILENAME];
  740.  
  741.     if (nntp_cache_dir == NULL) {
  742. #ifdef CACHE_DIRECTORY
  743.     nntp_cache_dir = CACHE_DIRECTORY;
  744. #else /* CACHE_DIRECTORY */
  745.     nntp_cache_dir = nn_directory;
  746. #endif /* CACHE_DIRECTORY */
  747.     }
  748.  
  749.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  750.     if (min_time > cptr->time) {
  751.         min_time = cptr->time;
  752.         lru = i;
  753.     }
  754.     cptr = &cache[lru];
  755.  
  756.     if (cptr->file_name == NULL) {
  757.     sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
  758.     cptr->file_name = copy_str(name);
  759.     } else
  760.     unlink(cptr->file_name);
  761.  
  762.     cptr->time = time_counter++;
  763.     return cptr;
  764. }
  765.  
  766. /*
  767.  * clean_cache: clean up the cache.
  768.  *
  769.  *     Removes all allocated files.
  770.  */
  771.  
  772. static void clean_cache()
  773. {
  774.     struct cache *cptr = cache;
  775.     int i;
  776.  
  777.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  778.     if (cptr->file_name)
  779.         unlink(cptr->file_name);
  780. }
  781.  
  782. /*
  783.  * nntp_check: Find out whether we need to use NNTP.
  784.  *
  785.  *     This is done by comparing the NNTP servers name with whatever
  786.  *     nn_gethostname() returns.
  787.  *    use_nntp and news_active are initialised as a side effect.
  788.  */
  789.  
  790. void
  791. nntp_check()
  792. {
  793.     char host[128];
  794.     char *server_real_name;
  795.     struct hostent *tmp;
  796.  
  797.     if (nntp_local_server) return;
  798.  
  799.     if (server_nntp)
  800.     strncpy(nntp_server,server_nntp,256);
  801.  
  802.     if (*nntp_server
  803. #ifndef NO_NNTP_SERVER_READ
  804.     || find_server()
  805. #endif
  806.     ) {
  807.     nn_gethostname(host, sizeof host);
  808.     tmp = gethostbyname(host);
  809.     if (tmp) {
  810.         strncpy(host, tmp->h_name, sizeof host);
  811.     }
  812.  
  813.     tmp = gethostbyname(nntp_server);
  814.     if (tmp) {
  815.         server_real_name = tmp->h_name;
  816.     } else {
  817.         server_real_name = nntp_server;
  818.     }
  819.     use_nntp = (strcmp(host, server_real_name) != 0);
  820.     } else {
  821.     use_nntp = 0;
  822.     }
  823.  
  824.     if (use_nntp) {
  825.     freeobj(news_active);
  826.     news_active = mk_file_name(nn_directory, "ACTIVE");
  827.     }
  828. }
  829.  
  830. /*
  831.  * nntp_no_post: Check to see whether posting is allowed.
  832.  */
  833.  
  834. int
  835. nntp_no_post()
  836. {
  837.     if (!is_connected && connect_server() < 0)
  838.     return 1;            /* If we cannot connect, neither can inews */
  839.     if (can_post == 0) {
  840.     msg("NNTP server does not allow postings from this host.  Sorry!");
  841.     return 1;
  842.     }
  843.     return 0;
  844. }
  845.  
  846.  
  847. /*
  848.  * nntp_set_group: set the server's current group.
  849.  *
  850.  *     Actual communication is delayed until an article is accessed, to
  851.  *     avoid unnecessary traffic.
  852.  */
  853.  
  854. int
  855. nntp_set_group(gh)
  856.     group_header *gh;
  857. {
  858.     group_hd = gh;
  859.     group_is_set = 0;
  860.     return 0;
  861. }
  862.  
  863. /*
  864.  * nntp_get_newsgroups:  get a copy of the newsgroups file.
  865.  *
  866.  *    Use the "LIST NEWSGROUPS" command to get the newsgroup descriptions.
  867.  *    Based on code from: olson%anchor.esd@sgi.com (Dave Olson)
  868.  */
  869.  
  870. FILE *nntp_get_newsgroups()
  871. {
  872.     char *new_name;
  873.     FILE *new = NULL;
  874.     int n;
  875.  
  876.     new_name = mktemp(relative(tmp_directory, ".nngrXXXXXX"));
  877.     new = open_file(new_name, OPEN_CREATE_RW|OPEN_UNLINK);
  878.     if (new == NULL) return NULL;
  879.  
  880.  again:
  881.     if (!is_connected && connect_server() < 0) goto err;
  882.  
  883.     fix_list_response = 1;
  884.     switch (n = ask_server(NULL, "LIST NEWSGROUPS")) {
  885.      case ERR_NOGROUP:        /* really ERR_FAULT */
  886.     goto err;
  887.  
  888.      case OK_GROUPS:
  889.     if (copy_text(new,"Group Names") == 0) {
  890.         if (fflush(new) != EOF) break;
  891.         fclose(new);
  892.     }
  893.     if (!nntp_failed) {
  894.         log_entry('N', "LIST NEWSGROUPS empty");
  895.         nntp_failed = 1;
  896.     }
  897.     return NULL;
  898.  
  899.      default:
  900.     if (try_again) goto again; /* Handle nntp server timeouts */
  901.     log_entry('N', "LIST NEWSGROUPS response: %d", n);
  902.     goto err;
  903.     }
  904.     rewind(new);
  905.     return new;
  906.  
  907.  err:
  908.     fclose(new);
  909.     return NULL;
  910. }
  911.  
  912. /*
  913.  * nntp_get_article: get an article from the server.
  914.  *
  915.  *     Returns a FILE pointer.
  916.  *    If necessary the server's current group is set.
  917.  *    The article (header and body) are copied into a file, so they
  918.  *    are seekable (nn likes that).
  919.  */
  920.  
  921. static char *mode_cmd[] = {
  922.     "ARTICLE",
  923.     "HEAD",
  924.     "BODY"
  925. };
  926.  
  927. FILE *nntp_get_article(article, mode)
  928. article_number article;
  929. int mode;    /* 0 => whole article, 1 => head only, 2 => body only */
  930. {
  931.     FILE *tmp;
  932.     static struct cache *cptr;
  933.     int n;
  934.     
  935.  again:
  936.     if (!is_connected && connect_server() < 0) {
  937.     return NULL;
  938.     }
  939.  
  940.     /*
  941.      * Set the server group to the current group
  942.      */
  943.     if (group_is_set == 0)
  944.     switch (do_set_group(0)) {
  945.      case -1:
  946.         return NULL;
  947.      case 0:
  948.         goto again;
  949.      case 1:
  950.         break;
  951.     }
  952.  
  953.     /*
  954.      * Search the cache for the requested article, and allocate a new
  955.      * slot if necessary (if appending body, we already got it).
  956.      */
  957.  
  958.     if (mode != 2) {
  959.     cptr = search_cache(article, group_hd);
  960.     if (cptr != NULL) goto out;
  961.     cptr = new_cache_slot();
  962.     }
  963.     
  964.     /*
  965.      * Copy the article.
  966.      */
  967.     switch (n = ask_server(NULL, "%s %ld", mode_cmd[mode], (long)article)) {
  968.      case OK_ARTICLE:
  969.      case OK_HEAD:
  970.     tmp = open_file(cptr->file_name, OPEN_CREATE|MUST_EXIST);
  971.     if (copy_text(tmp,"Article ") < 0)
  972.         return NULL;
  973.  
  974.     if (mode == 1 && !last_copy_blank)
  975.         fputc(NL, tmp); /* add blank line after header */
  976.  
  977.     if (fclose(tmp) == EOF) goto err;
  978.     cptr->art = article;
  979.     cptr->grp = group_hd;
  980.     goto out;
  981.  
  982.      case OK_BODY:
  983.     tmp = open_file(cptr->file_name, OPEN_APPEND|MUST_EXIST);
  984.     fseek(tmp, (off_t)0, 2);
  985.     if (copy_text(tmp,"Article") < 0)
  986.         return NULL;
  987.     if (fclose(tmp) == EOF) goto err;
  988.     goto out;
  989.     
  990.      case ERR_NOARTIG:
  991.              /* Matt Heffron: ANUNEWS on VMS uses no such article error */
  992.      case ERR_NOART:
  993.     return NULL;
  994.  
  995.      default:
  996.     if (try_again) goto again; /* Handle nntp server timeouts */
  997.     /* Matt Heffron: Which group? */
  998.     log_entry('N', "ARTICLE %ld response: %d (in Group %s)",
  999.                     (long)article, n, group_hd->group_name);
  1000.     nntp_failed = 1;
  1001.     return NULL;
  1002.     }
  1003.  
  1004.  out:
  1005.     return open_file(cptr->file_name, OPEN_READ|MUST_EXIST);
  1006.  
  1007.  err:
  1008.     sys_error('N', "Cannot write temporary file %s", cptr->file_name);
  1009.     return NULL;    /* ode to the fussy compiler */
  1010. }
  1011.  
  1012. /*
  1013.  *    Return local file name holding article
  1014.  */
  1015.  
  1016. char *nntp_get_filename(art, gh)
  1017. article_number art;
  1018. group_header *gh;
  1019. {
  1020.     struct cache *cptr;
  1021.  
  1022.     cptr = search_cache(art, gh);
  1023.  
  1024.     return cptr == NULL ? NULL : cptr->file_name;
  1025. }
  1026.  
  1027. /*
  1028.  * nntp_close_server: close the connection to the server.
  1029.  */
  1030.  
  1031. void
  1032. nntp_close_server()
  1033. {
  1034.     if (!is_connected)
  1035.     return;
  1036.  
  1037.     if (!nntp_failed) {            /* avoid infinite recursion */
  1038.     int n;
  1039.  
  1040.     n = ask_server(NULL, "QUIT");
  1041.     if (n != OK_GOODBYE)
  1042.         ;                /* WHAT NOW ??? */
  1043.     }
  1044.  
  1045.     (void) fclose(nntp_out);
  1046.     (void) fclose(nntp_in);
  1047.  
  1048.     is_connected = 0;
  1049. }
  1050.  
  1051. /*
  1052.  * nntp_cleanup:  clean up after an nntp session.
  1053.  *
  1054.  *    Called from nn_exit().
  1055.  */
  1056.  
  1057. void
  1058. nntp_cleanup()
  1059. {
  1060.     if (is_connected)
  1061.     nntp_close_server();
  1062.     clean_cache();
  1063. }
  1064.  
  1065.  
  1066. /*************************************************************/
  1067.  
  1068. /*
  1069. ** Prime the nntp server to snarf the overview file for a newsgroup.
  1070. ** Sends the XOVER command and prepares to read the result.
  1071. */
  1072. struct novgroup *
  1073. nntp_get_overview(gh, first, last)
  1074. group_header *gh;
  1075. article_number first, last;
  1076. {
  1077.   int    n;
  1078.  
  1079. again:
  1080.   nntp_set_group(gh);
  1081.   switch (do_set_group(first)) {
  1082.   case -1:
  1083.     return NULL;
  1084.   case 0:
  1085.     goto again;
  1086.   }
  1087.  
  1088.   n = ask_server(NULL, "XOVER %d-%d", first, last);
  1089.   switch (n) {
  1090.     
  1091.   case OK_NOV:
  1092.     return novstream(nntp_in);
  1093.  
  1094.   default:
  1095.     if (try_again) 
  1096.       goto again; /* Handle nntp server timeouts */
  1097.     log_entry('N', "XOVER response: %d", n);
  1098.     return NULL;
  1099.  
  1100.   }
  1101. }
  1102.  
  1103. /*
  1104.  * nntp_fopen_list(cmd):  Send some variant of a LIST command to the
  1105.  * NNTP server.  returns NULL if the file to be LISTed doesn't exist
  1106.  * on the server, else returns the nntp_in FILE descriptor, thus
  1107.  * simulating fopen().
  1108.  * nntp_fgets() is later used to read a line from the nntp_in FILE.
  1109.  */
  1110.  
  1111. FILE *
  1112. nntp_fopen_list(cmd)
  1113. char *cmd;
  1114. {
  1115.   int    n;
  1116.  
  1117. again:
  1118.   if (!is_connected && connect_server() < 0) 
  1119.     return NULL;
  1120.  
  1121.   fix_list_response = 1;
  1122.   switch (n = ask_server(NULL, cmd)) {
  1123.   case ERR_NOGROUP:        /* really ERR_FAULT - no such file on server */
  1124.     return NULL;
  1125.  
  1126.   case OK_GROUPS:        /* aka NNTP_LIST_FOLLOWS_VAL */
  1127.     return nntp_in;
  1128.  
  1129.   default:
  1130.     if (try_again) goto again;    /* Handle nntp server timeouts */
  1131.     log_entry('N', "`%s' response: %d", cmd, n);
  1132.     return NULL;
  1133.   }
  1134. }
  1135.  
  1136. FILE *
  1137. nntp_fopen()
  1138. {
  1139.   int    n;
  1140.  
  1141.   if (!is_connected && connect_server() < 0) 
  1142.       return NULL;
  1143.   else
  1144.       return nntp_in;
  1145. }
  1146.  
  1147. /*
  1148.  * nntp_fgets() - Get a line from a file stored on the NNTP server.
  1149.  * Strips any hidden "." at beginning of line and returns a pointer to
  1150.  * the line.  line will be terminated by NL NUL.
  1151.  * Returns NULL when NNTP sends the terminating "." to indicate EOF.
  1152.  */
  1153. char *
  1154. nntp_fgets(buf, bufsize)
  1155. char *buf;
  1156. int bufsize;
  1157. {
  1158.     char *cp;
  1159.     register int size;
  1160.  
  1161.     if ((size = get_server_line(buf, bufsize - 1)) < 0)
  1162.     return NULL;    /* Can't happen with NOV (we'd rather die first) */
  1163.  
  1164.     cp = buf;
  1165.     if (*cp == '.') {
  1166.         if (*++cp == '\0')
  1167.         return NULL;
  1168.     }
  1169.     cp[size]   = '\n';
  1170.     cp[size+1] = '\0';
  1171.     return cp;
  1172. }
  1173.  
  1174.  
  1175. int send_user_info()
  1176. {
  1177.       int n;
  1178.  
  1179.       char auth_path[255];
  1180.       char auth_user[50],auth_pass[50];
  1181.       FILE *fp;
  1182.  
  1183.       fp=fopen(relative(nn_directory, "authinfo"),"r");
  1184.       if (fp!=NULL) {
  1185.               int i,j;
  1186.               n=0;i=0;
  1187.               while((i=fgetc(fp))!=EOF) {
  1188.                       if (i==' ') {
  1189.                               auth_user[n]='\0';
  1190.                               break;
  1191.                       }
  1192.                       auth_user[n++]=(char)i;
  1193.               }
  1194.               auth_user[n]='\0';
  1195.  
  1196.               n=0;
  1197.               while((i=fgetc(fp))!=EOF) {
  1198.                       if (i=='\n') {
  1199.                               auth_pass[n]='\0';
  1200.                               break;
  1201.                       }
  1202.                       auth_pass[n++]=(char)i;
  1203.               }
  1204.               fclose(fp);
  1205.  
  1206.               ask_server(NULL, "authinfo user %s", auth_user);
  1207.               n = ask_server(NULL, "authinfo pass %s", auth_pass);  
  1208.  
  1209.               if (n== 281) { /* OK */
  1210.                       msg(" Authentication to server succesfull ");
  1211.               }
  1212.               else {
  1213.                       msg (" Authentication to server unsuccesfull ");
  1214.                       return -1;
  1215.               }
  1216.                       
  1217.       }
  1218.                       return 0;
  1219. }
  1220.  
  1221.   
  1222. #endif /* NNTP */
  1223.  
  1224.